﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using MvcPrinciples.Models;

namespace MvcPrinciples.Logic
{
	internal class AppleStore// : IDisposable, IStore
	{
		private readonly SqlConnection _connection;
		private readonly object _lock = new object();

		public AppleStore(string connectionString, SqlCredential credential)
		{
			var cstring = string.IsNullOrEmpty(connectionString) ? ConfigurationManager.ConnectionStrings["default"].ConnectionString : connectionString;

			_connection = new SqlConnection(cstring, credential);
		}

		public bool AddPerson(string name, DateTime birthDate)
		{
			if (string.IsNullOrEmpty(name))
				throw new ArgumentNullException("name");

			var args = new[]
			{
				new SqlParameter { SqlDbType = SqlDbType.NVarChar, ParameterName = "@Name", Value = name },
				new SqlParameter { SqlDbType = SqlDbType.Date, ParameterName = "@BirthDate", Value = birthDate }
			};

			return ExecuteCommand(args, "AddPerson");
		}

		public bool AddPersonReward(Guid person, Guid reward)
		{
			var args = new[]
			{
				new SqlParameter { SqlDbType = SqlDbType.UniqueIdentifier, ParameterName = "@PersonId", Value = person },
				new SqlParameter { SqlDbType = SqlDbType.UniqueIdentifier, ParameterName = "@RewardId", Value = reward }
			};

			return ExecuteCommand(args, "AddPersonReward");
		}

		public bool AddReward(string title, string description, Guid image)
		{
			if (string.IsNullOrEmpty(title))
				throw new ArgumentNullException("title");

			if (string.IsNullOrEmpty(description))
				throw new ArgumentNullException("description");

			var args = new[]
			{
				new SqlParameter { SqlDbType = SqlDbType.NVarChar, ParameterName = "@Title", Value = title },
				new SqlParameter { SqlDbType = SqlDbType.NVarChar, ParameterName = "@Description", Value = description },
				new SqlParameter { SqlDbType = SqlDbType.UniqueIdentifier, ParameterName = "@Image", Value = image }
			};

			return ExecuteCommand(args, "AddReward");
		}

		public void Dispose()
		{
			if (_connection != null)
				_connection.Dispose();
		}

		public void GetPagePersons()
		{
			throw new NotImplementedException();
		}

		public PersonReward GetPersonRewards(Guid personId)
		{
			throw new NotImplementedException();
		}

		public List<Person> GetPersons()
		{
			List<object[]> result;
			var persons = new List<Person>();

			if (!ExecuteCommand(null, "GetPersons", out result))
				return persons;

			result.ForEach(x =>
			{
				var pid = (Guid) x[0];
				var pname = (string) x[1];
				var pbd = (DateTime) x[2];

				persons.Add(new Person {BirthDate = pbd, Id = pid, Name = pname});
			});

			return persons;
		}

		public List<Reward> GetRewards()
		{
			List<object[]> result;
			var rewards = new List<Reward>();

			if (!ExecuteCommand(null, "GetRewards", out result))
				return rewards;

			result.ForEach(x =>
			{
				var rid = (Guid)x[0];
				var rtitle = (string)x[1];
				var rdesc = (string)x[2];

				rewards.Add(new Reward { Description = rdesc, Id = rid, Title = rtitle });
			});

			return rewards;
		}

		private bool ExecuteCommand(SqlParameter[] parameters, string name)
		{
			lock (_lock)
			{
				if (!OpenConnection())
					return false;

				if (parameters == null)
					throw new ArgumentNullException("parameters");

				if (string.IsNullOrEmpty(name))
					throw new ArgumentNullException("name");

				using (var command = new SqlCommand(name, _connection) { CommandType = CommandType.StoredProcedure })
				{
					command.Parameters.AddRange(parameters);
					command.ExecuteNonQuery();
				}
			}

			return true;
		}

		private bool ExecuteCommand(SqlParameter[] parameters, string name, out List<object[]> result)
		{
			result = null;

			if (string.IsNullOrEmpty(name))
				throw new ArgumentNullException("name");
			
			parameters = parameters ?? new SqlParameter[0];

			lock (_lock)
			{
				if (!OpenConnection())
					return false;

				using (var command = new SqlCommand(name, _connection) { CommandType = CommandType.StoredProcedure })
				{
					command.Parameters.AddRange(parameters);
					using (var reader = command.ExecuteReader())
					{
						result = new List<object[]>();

						while (reader.Read())
						{
							var values = new object[reader.FieldCount];
							reader.GetValues(values);
							result.Add(values);
						}
					}
				}
			}
			return true;
		}

		private bool OpenConnection()
		{
			switch (_connection.State)
			{
				case ConnectionState.Broken: _connection.Close(); _connection.Open();
					return true;
				case ConnectionState.Closed: _connection.Open();
					return true;
				case ConnectionState.Open:
					return true;
			}

			return false;
		}
	}
}